/**
 * 
 */
package com.khaled.protclass.featureselection.configuration;

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.khaled.protclass.featureselection.common.Constants;
import com.khaled.protclass.model.base.BaseFeature;
import com.khaled.protclass.model.bean.protein.ProteinType;

/**
 * @author hassankh
 * 
 */
public final class Configuration
{
	/** Logger. */
	private static Logger logger = Logger.getLogger(Configuration.class);

	private static Configuration configuration;

	private Input input;

	private ArrayList<BaseFeature> features;

	private Configuration() throws Exception
	{
		loadConfiguration();
	}

	private void loadConfiguration() throws Exception
	{
		logger.info("loadConfiguration starts.");

		SAXReader reader = new SAXReader();
		Document document = reader.read(new File(Constants.CONFIGURATION_FILE_PATH));
		Element root = document.getRootElement();

		loadInput(root.element(Constants.XML_NODES_INPUT));
		loadFeatures(root.element(Constants.XML_NODES_FEATURES), root.element(Constants.XML_NODES_FEATURES_SETUP));

		logger.info("loadConfiguration ends.");
	}

	private void loadInput(Element element) throws Exception
	{
		String class1_path = element.element(Constants.XML_NODES_INPUT_CLASS1_PATH).getStringValue();
		String class2_path = element.element(Constants.XML_NODES_INPUT_CLASS2_PATH).getStringValue();
		String output_path = element.element(Constants.XML_NODES_INPUT_OUTPUT_PATH).getStringValue();
		String proteinType = element.element(Constants.XML_NODES_INPUT_PROTEIN_TYPE).getStringValue();
		String relationName = element.element(Constants.XML_NODES_INPUT_RELATION_NAME).getStringValue();

		this.input = new Input(class1_path, class2_path, output_path, relationName, new ProteinType(proteinType));
	}

	private void loadFeatures(Element features_Element, Element featuresSetup_Element) throws Exception
	{
		this.features = new ArrayList<BaseFeature>();

		for (int i = 0; i < features_Element.elements().size(); i++)
		{
			Element f_Ele = (Element) features_Element.elements().get(i);
			String f_Name = f_Ele.getName();

			Element featureAttributes_Element = featuresSetup_Element.element(f_Name);

			if (featureAttributes_Element == null)
			{
				throw new Exception("There is no class definition for feaure:" + f_Name + "\n");
			}
			else
			{
				BaseFeature feature = getFeature(featureAttributes_Element);
				this.features.add(feature);
			}
		}

	}

	private BaseFeature getFeature(Element featureAttributes_Element) throws Exception
	{
		String f_Class = featureAttributes_Element.getStringValue();
		Hashtable<String, String> featureAttributes = new Hashtable<String, String>();
		BaseFeature feature = null;

		for (int i = 0; i < featureAttributes_Element.elements().size(); i++)
		{
			Element att_Ele = (Element) featureAttributes_Element.elements().get(i);
			String att_Name = att_Ele.getName();
			String att_Value = att_Ele.getStringValue();

			featureAttributes.put(att_Name, att_Value);
		}

		// define Feature Class

		if (featureAttributes.containsKey(Constants.XML_NODES_CLASS))
		{
			String classValue = featureAttributes.get(Constants.XML_NODES_CLASS);

			@SuppressWarnings("rawtypes")
			Class cls = Class.forName(classValue);
			feature = (BaseFeature) cls.newInstance();
			feature.setProperties(featureAttributes);

			feature.init();
		}
		else
		{
			throw new Exception("There is no class definition for feaure:" + f_Class + "\n");
		}

		return feature;
	}

	public static Configuration getInstance()
	{
		if (configuration == null)
		{
			try
			{
				configuration = new Configuration();
			}
			catch (Exception e)
			{
				System.out.println("Error While loading configuration parameters:");
				System.out.println("Stake Trace:");
				e.printStackTrace();
				System.out.println("Exception Message:");
				System.out.println(e.getMessage());
				System.out.println("The Application will be closed.");

				System.exit(0);
			}
		}
		return configuration;
	}

	@Override
	public String toString()
	{
		String config_String = "";

		config_String += "Input:\n";

		config_String += "class1-path: " + getInput().getClass1_path() + "\n";
		config_String += "class2-path:" + getInput().getClass2_path() + "\n";
		config_String += "output-path:" + getInput().getOutputFilePath() + "\n";
		config_String += "relation-name:" + getInput().getRelation_name() + "\n";
		config_String += "protein-type (Sequence):" + getInput().getProteinType().isSequence() + "\n";
		config_String += "protein-type (Structure):" + getInput().getProteinType().isStructure() + "\n";

		config_String += "=====================================================================\n";

		config_String += "Features:\n";

		for (BaseFeature feature : features)
		{
			config_String += feature.getName() + "\n";

			for (String key : feature.getProperties().keySet())
			{
				config_String += "    Attribute(" + key + ")=" + feature.getProperties().get(key) + "\n";
			}
		}

		return config_String;
	}

	public Input getInput()
	{
		return input;
	}

	public ArrayList<BaseFeature> getFeatures()
	{
		return features;
	}

	public BaseFeature getFeature(String featureName)
	{
		for (BaseFeature f : this.features)
		{
			if (featureName.equals(f.getName())) { return f; }
		}
		return null;
	}

	public static void main(String[] args)
	{
		Configuration config = Configuration.getInstance();

		System.out.println(config);
	}

}
